# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: xguo

import collections
import datetime
import enum
import json
import schedule

import requests
import tornado.httpclient

from recordclass import recordclass

# Suppress schedule logging message
schedule.logger.disabled = True

PrivateExchangeMessage = collections.namedtuple('PrivateExchangeMessage',
                                                [
                                                    'msg_type',
                                                    'data',
                                                    'received_timestamp',
                                                    'request_params',
                                                    'request_timestamp',
                                                ])


class PrivateExchangeMessageEncoder(json.JSONEncoder):
  def default(self, obj):
    if isinstance(obj, PrivateExchangeMessage):
      # noinspection PyProtectedMember
      return obj._asdict()
    elif isinstance(obj, enum.Enum):
      return obj.name
    elif isinstance(obj, requests.Response):
      if obj.request is None:
        body = ''
      else:
        body = obj.request.body
      if isinstance(body, bytes):
        body = body.decode()
      elif isinstance(body, str):
        body = body
      else:
        body = str(body)

      return {
          'status_code': obj.status_code,
          'url': obj.url,
          'headers': dict(obj.headers),
          'content': obj.text,
          'request_content': body,
      }
    # Let the base class default method raise the TypeError
    return json.JSONEncoder.default(self, obj)


RestOrderInternal = recordclass('RestOrderInternal',
                                [
                                    'timestamp',
                                    'exchange_order_id',
                                    'cancel_confirmed',
                                    'cancel_sent_time',
                                    'fully_filled',
                                    'fill',
                                    'fee',
                                    'status',
                                    'submit_ack_timestamp',
                                    'last_seen_timestamp',
                                    'num_not_seen_times',
                                    'leverage',
                                    'post_only',
                                ])


def create_order_internal(
    *,
    timestamp=None,
    exchange_order_id=None,
    cancel_confirmed=False,
    cancel_sent_time=None,
    fully_filled=False,
    fill=None,
    fee=None,
    status=None,
    submit_ack_timestamp=None,
    last_seen_timestamp=None,
    num_not_seen_times=0,
    leverage=None,
    post_only=False,
):
  return RestOrderInternal(
      timestamp=timestamp,
      exchange_order_id=exchange_order_id,
      cancel_confirmed=cancel_confirmed,
      cancel_sent_time=cancel_sent_time,
      fully_filled=fully_filled,
      fill=fill,
      fee=fee,
      status=status,
      submit_ack_timestamp=submit_ack_timestamp,
      last_seen_timestamp=last_seen_timestamp,
      num_not_seen_times=num_not_seen_times,
      leverage=leverage,
      post_only=post_only,
  )


def pretty_json(data, pretty=True):
  if isinstance(data, (str, bytes)):
    data = json.load(data)
  if pretty:
    return json.dumps(data, indent=2, default=str)
  else:
    return json.dumps(data, default=str)


def convert_to_requests_response(response):
  if isinstance(response, requests.Response):
    return response
  elif isinstance(response, tornado.httpclient.HTTPResponse):
    new_response = requests.Response()
    new_response.headers = response.headers
    new_response.status_code = response.code
    new_response.raw = response.buffer
    new_response.elapsed = datetime.timedelta(seconds=response.request_time)
    return new_response
  else:
    return response


# An easy hack, another option is using chained futures, which is
# a bit harder to implement correctly.
def convert_rest_response_future(future):
  old_result = future.result

  def new_result():
    return convert_to_requests_response(old_result())

  future.result = new_result
  return future
